﻿/**

 * Copyright (c) 2015-2016, FastDev 刘强 (fastdev@163.com) & Quincy.

 *

 * Licensed under the Apache License, Version 2.0 (the "License");

 * you may not use this file except in compliance with the License.

 * You may obtain a copy of the License at

 *

 *      http://www.apache.org/licenses/LICENSE-2.0

 *

 * Unless required by applicable law or agreed to in writing, software

 * distributed under the License is distributed on an "AS IS" BASIS,

 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

 * See the License for the specific language governing permissions and

 * limitations under the License.

 */

using MsgPack.Serialization;
using OF.Cache2.Entity;
using StackExchange.Redis;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using OF.Cache2.Core;
using OF.Cache2.Entity;
using OF.Cache2.Redis;

namespace OF.Cache2.Impl
{
    public class FIFODict<T> where T : class, ICacheItem
    {
        private int maxItemCount;

        private object lockObj = new object();
        private Dictionary<string, LinkItem<T>> dataDict = new Dictionary<string, LinkItem<T>>();
        private HeadTailLink<T> dataLink = new HeadTailLink<T>();

        public FIFODict(int maxItemCount)
        {
            this.maxItemCount = maxItemCount;
        }

        public bool TryGetValue(string key, out T value)
        {
            lock (lockObj)
            {
                LinkItem<T> findItem = null;
                bool hasItem = dataDict.TryGetValue(key, out findItem);
                if (!hasItem)
                {
                    value = null;
                    return false;
                }
                DateTime expireDate = findItem.Data.GetExpireDate();
                if (expireDate <= DateTime.Now)
                {
                    RemoveCore(key);
                    value = null;
                    return false;
                }
                dataLink.MoveItemHead(findItem);
                value = findItem.Data;
                return true;
            }
        }

        public void SetValue(T value)
        {
            string key = value.GetKey();
            lock (lockObj)
            {
                RemoveCore(key);
                if (dataDict.Count >= this.maxItemCount)
                {
                    T removedItem = dataLink.RemoveTail();
                    if (removedItem != null)
                    {
                        dataDict.Remove(removedItem.GetKey());
                    }
                }
                LinkItem<T> linkItem = new LinkItem<T> { Data = value };
                dataDict[key] = linkItem;
                dataLink.Add(linkItem);
            }
        }

        private bool RemoveCore(string key)
        {
            if (dataDict.ContainsKey(key))
            {
                var dataItem = dataDict[key];
                dataLink.RemoveItem(dataItem);
                dataDict.Remove(key);
                return true;
            }
            return false;
        }

        public bool Remove(string key)
        {
            lock (lockObj)
            {
                return RemoveCore(key);
            }
        }
    }

}
